﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MediaBrowser.Library;
using MediaBrowser.Library.Metadata;
using FullRefreshService.Logging;
using System.Timers;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.Win32;

namespace FullRefreshService
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Private Members

        private static readonly object lockObj = new object();
        private static List<string> messages;
        private static bool _isRunning = false;
        private static bool _isExecuting = false;
        private static System.Windows.Forms.Timer _timer;

        const string _appName = "MediaBrowserMetadataRefresh";
        RegistryKey _rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);

        #endregion

        public MainWindow()
        {
            InitializeComponent();

            SetupTaskbarStuff();

            messages = new List<string>();
            UpdateLogger logger = new UpdateLogger();
            logger.Updated += new EventHandler<LoggingEventArgs>(logger_Updated);
            Logger.Instance.RegisterLogger(logger);

            // get config values for bool vals
            GetConfigValues();

            // Init values...
            SetGuiValues();

            if (State.StartServiceOnStartup)
            {
                StartService();
            }

            if (State.RunRefreshOnStartup)
            {
                PerformRefresh();
            }
        }

        private void SetupTaskbarStuff()
        {
            System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon();
            System.Drawing.Icon theIcon = new System.Drawing.Icon(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("FullRefreshService.Icon.ico"));
            ni.Icon = theIcon;
            ni.Visible = true;
            ni.DoubleClick +=
            delegate(object sender, EventArgs args)
            {
                this.Show();
                this.WindowState = WindowState.Normal;
            };
        }

        protected override void OnStateChanged(EventArgs e)
        {
            if (WindowState == WindowState.Minimized)
                this.Hide();
            base.OnStateChanged(e);
        }

        private void SetWindowsStartupCheckbox()
        {
            if (_rkApp.GetValue(_appName) == null)
            {
                // The value doesn't exist, the application is not set to run at startup
                startServiceOnStartupChk.IsChecked = false;

            }
            else
            {
                // The value exists, the application is set to run at startup
                startServiceOnStartupChk.IsChecked = true;
            }
        }

        private void SetGuiValues()
        {
            hoursTxt.Text = State.Hours.ToString();
            maxLinesTxt.Text = State.MaxMessages.ToString();
            timePicker.Value = State.Time;

            onlyRefreshWhenClosedChk.IsChecked = State.OnlyRefreshWhenClosed;
            closeMcBeforeRefreshChk.IsChecked = State.CloseMcBeforeRefresh;
            mediaBrowserRefreshOpen.IsChecked = State.ReOpenMediaBrowser;
            mediaCenterRefreshOpen.IsChecked = State.ReOpenMediaCenter;
            startupRefreshChk.IsChecked = State.RunRefreshOnStartup;
            outputToFileChk.IsChecked = State.OutputToFile;

            SetWindowsStartupCheckbox();
        }

        private void GetConfigValues()
        {
            State.MaxMessages = ConfigHelper.GetValue(ConfigOption.MaximumLines, DefaultValues.MaxLines);
            State.Hours = ConfigHelper.GetValue(ConfigOption.HourlyFrequency, DefaultValues.HourlyFrequency);
            State.Time = ConfigHelper.GetValue(ConfigOption.Time, DefaultValues.Time);

            State.OnlyRefreshWhenClosed = ConfigHelper.GetValue(ConfigOption.OnlyRefreshWhenClosed, DefaultValues.OnlyRefreshWhenClosed);
            State.CloseMcBeforeRefresh = ConfigHelper.GetValue(ConfigOption.CloseMcBeforeRefresh, DefaultValues.CloseMcBeforeRefresh);
            State.ReOpenMediaBrowser = ConfigHelper.GetValue(ConfigOption.ReOpenMediaBrowser, DefaultValues.ReOpenMediaBrowser);
            State.ReOpenMediaCenter = ConfigHelper.GetValue(ConfigOption.ReOpenMediaCenter, DefaultValues.ReOpenMediaCenter);
            State.StartServiceOnStartup = ConfigHelper.GetValue(ConfigOption.StartServiceOnStartup, DefaultValues.StartServiceOnStartup);
            State.RunRefreshOnStartup = ConfigHelper.GetValue(ConfigOption.RunRefreshOnStartup, DefaultValues.RunRefreshOnStartup);
            State.OutputToFile = ConfigHelper.GetValue(ConfigOption.OutputToFile, DefaultValues.OutputToFile);
        }

        void logger_Updated(object sender, LoggingEventArgs e)
        {
            lock (lockObj)
            {
                if (messages.Count > State.MaxMessages)
                {
                    messages.RemoveAt(0);
                }

                messages.Add(e.Message);
                StringBuilder builder = new StringBuilder();
                foreach (var message in messages)
                {
                    builder.AppendLine(message);
                }

                Dispatcher.BeginInvoke(new ThreadStart(() =>
                {
                    messagesTxt.Text = builder.ToString();
                }));
            }
        }

        private void PerformRefresh()
        {
            if (!_isExecuting)
            {
                State.RunStartTime = DateTime.Now;
                messages.Clear();

                bool isMcOpen = IsMediaCenterOpen();
                if (State.CloseMcBeforeRefresh && isMcOpen)
                {
                    WriteLine("Closing Media Center");
                    CloseMediaCenter();
                    isMcOpen = IsMediaCenterOpen();
                }

                if ((State.OnlyRefreshWhenClosed && !isMcOpen) || !State.OnlyRefreshWhenClosed)
                {
                    MainMethod();
                }
                else
                {
                    WriteLine("{0}: Unable to perform refresh as Media Center is open.", DateTime.Now);
                }
            }
            else
            {
                MessageBox.Show("Refresh is already running..");
            }
        }

        private static void MainMethod()
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                _isExecuting = true;

                WriteLine(@"The refresh process runs in 3 steps: ");
                WriteLine("");
                WriteLine(@"1. Your library is scanned for new files and missing files. They are added/removed with no metadata");
                WriteLine(@"2. Your library is scanned a second time, this time we try to populate metadata for fast providers (ones that do not depend on the Internet, or a large amount of processing power.");
                WriteLine(@"3. Your library is scanned a third time, this time all providers kick in.");

                try
                {
                    FullRefresh.PerformFullRefresh(Kernel.Instance.RootFolder, MetadataRefreshOptions.Default);
                    WriteLine("We are done");
                    WriteLine("");
                    WriteLine("Validation took: " + (new DateTime(FullRefresh.ValidationTime.Ticks)).ToString("HH:mm:ss"));
                    WriteLine("Fast metadata took: " + (new DateTime(FullRefresh.FastMetadataTime.Ticks)).ToString("HH:mm:ss"));
                    WriteLine("Slow metadata took: " + (new DateTime(FullRefresh.SlowMetadataTime.Ticks)).ToString("HH:mm:ss"));

                    WriteLine(string.Empty);
                    WriteLine("Total items in your library: {0}", FullRefresh.TotalItems);
                    WriteLine("Fast metadata changed on {0} item/s", FullRefresh.FastMetadataChanged);
                    WriteLine("Slow metadata changed on {0} item/s", FullRefresh.SlowMetadataChanged);
                    WriteLine(string.Empty);
                }
                catch (Exception ex)
                {
                    WriteLine("Error performing refresh: {0}", ex.Message);
                }

                // start media browser or media center after successful boot.
                if (State.ReOpenMediaBrowser)
                {
                    WriteLine("Opening MediaBrowser");
                    OpenMediaBrowser();
                }
                else if (State.ReOpenMediaCenter)
                {
                    WriteLine("Opening Media Center");
                    OpenMediaCenter();
                }

                _isExecuting = false;
            });
        }

        private static void CloseMediaCenter()
        {
            using (Process process = GetMediaCenterProcess())
            {
                if (process != null)
                {
                    process.CloseMainWindow();
                    process.WaitForExit();
                }
            }
        }

        private static bool IsMediaCenterOpen()
        {
            using (Process process = GetMediaCenterProcess())
            {
                return process != null;
            }
        }

        private static void OpenMediaCenter()
        {
            StartMediaCenterProcess(null);
        }

        private static void OpenMediaBrowser()
        {
            StartMediaCenterProcess(@"/entrypoint:{CE32C570-4BEC-4aeb-AD1D-CF47B91DE0B2}\{FC9ABCCC-36CB-47ac-8BAB-03E8EF5F6F22}");
        }

        private static void StartMediaCenterProcess(string cmdLineSwitch)
        {
            string dir = Environment.GetEnvironmentVariable("SystemRoot");
            string mcePath = System.IO.Path.Combine(dir, @"ehome\ehshell.exe");

            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = mcePath;
            startInfo.Arguments = string.Format("/nostartupanimation {0}", cmdLineSwitch);
            Process.Start(startInfo);
        }

        private static Process GetMediaCenterProcess()
        {
            Process retVal = null;

            var processes = Process.GetProcessesByName("ehshell");
            if (processes.Any())
            {
                retVal = processes.First();
            }

            return retVal;
        }

        private void SaveValuesToConfig()
        {
            EnsureConfigExistance();

            ConfigHelper.SetValue(ConfigOption.HourlyFrequency, State.Hours);
            ConfigHelper.SetValue(ConfigOption.Time, State.Time);
            ConfigHelper.SetValue(ConfigOption.OnlyRefreshWhenClosed, State.OnlyRefreshWhenClosed);
            ConfigHelper.SetValue(ConfigOption.CloseMcBeforeRefresh, State.CloseMcBeforeRefresh);
            ConfigHelper.SetValue(ConfigOption.ReOpenMediaBrowser, State.ReOpenMediaBrowser);
            ConfigHelper.SetValue(ConfigOption.ReOpenMediaCenter, State.ReOpenMediaCenter);
            ConfigHelper.SetValue(ConfigOption.StartServiceOnStartup, startServiceOnStartupChk.IsChecked);
            ConfigHelper.SetValue(ConfigOption.RunRefreshOnStartup, startupRefreshChk.IsChecked);
            ConfigHelper.SetValue(ConfigOption.MaximumLines, State.MaxMessages);
            ConfigHelper.SetValue(ConfigOption.OutputToFile, outputToFileChk.IsChecked);
        }

        private void EnsureConfigExistance()
        {
            string fileName = Assembly.GetExecutingAssembly().Location;
            string configFile = string.Format("{0}.config", fileName);

            if (!File.Exists(configFile))
            {
                FileLogger.WriteLineBase(new FileInfo(configFile), @"<?xml version=""1.0""?><configuration><appSettings></appSettings></configuration>");
            }
        }

        private bool IsValidInput()
        {
            bool isValid = false;
            int dummy1;
            int dummy2;
            isValid = int.TryParse(hoursTxt.Text, out dummy1) &&
                      int.TryParse(maxLinesTxt.Text, out dummy2);
            return isValid;
        }

        private void startBtn_Click(object sender, RoutedEventArgs e)
        {
            StartService();
        }

        private void StartService()
        {
            bool isValidChange = false;

            if (_isRunning)
            {
                startBtn.Content = "Start Service";
                // stop it
                _timer.Enabled = false;
                isValidChange = true;
            }
            else
            {
                // validate values..
                isValidChange = IsValidInput();

                if (isValidChange)
                {
                    startBtn.Content = "Stop Service";

                    int hours = Convert.ToInt32(hoursTxt.Text);
                    DateTime now = DateTime.Now;
                    DateTime withTime = timePicker.Value;
                    while (withTime < now)
                        withTime = withTime.AddDays(1);
                    TimeSpan timeDiff = withTime - now;

                    // set the timer...
                    int interval = (int)timeDiff.TotalMilliseconds;
                    _timer = new System.Windows.Forms.Timer();
                    _timer.Interval = interval;
                    _timer.Tick += (a, b) =>
                    {
                        PerformRefresh();
                        _timer.Interval = (int)TimeSpan.FromHours(hours).TotalMilliseconds;
                    };
                    _timer.Enabled = true;
                }
            }

            if (isValidChange)
            {
                hoursTxt.IsEnabled = _isRunning;
                timePicker.Enabled = _isRunning;
                
                _isRunning = !_isRunning;
            }
        }

        private void refreshNowBtn_Click(object sender, RoutedEventArgs e)
        {
            if (IsValidInput())
            {
                PerformRefresh();
            }
            else
            {
                MessageBox.Show("Invalid entries");
            }
        }

        private static void WriteLine(string message, params object[] args)
        {
            Logging.Logger.Instance.WriteLine(string.Format(message, args));
        }

        #region Events

        private void onlyRefreshWhenClosedChk_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.OnlyRefreshWhenClosed = true;
        }

        private void closeMcBeforeRefreshChk_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.CloseMcBeforeRefresh = true;
        }

        private void mediaBrowserRefreshOpen_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.ReOpenMediaBrowser = true;
        }

        private void mediaCenterRefreshOpen_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.ReOpenMediaCenter = true;
        }

        private void startupRefreshChk_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.RunRefreshOnStartup = true;
        }

        private void startServiceOnStartupChk_Checked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.StartServiceOnStartup = true;
            SetServiceStart();
        }

        private void SetServiceStart()
        {
            if (State.StartServiceOnStartup)
            {
                // Add the value in the registry so that the application runs at startup
                _rkApp.SetValue(_appName, Assembly.GetExecutingAssembly().Location);
            }
            else
            {
                _rkApp.DeleteValue(_appName);
            }
        }

        private void startServiceOnStartupChk_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.StartServiceOnStartup = false;
            SetServiceStart();
        }

        private void startupRefreshChk_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.RunRefreshOnStartup = false;
        }

        private void mediaBrowserRefreshOpen_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.ReOpenMediaBrowser = false;
        }

        private void mediaCenterRefreshOpen_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.ReOpenMediaCenter = false;
        }

        private void closeMcBeforeRefreshChk_Unchecked(object sender, RoutedEventArgs e)
        {
            if (!IsInitialized) return;
            State.CloseMcBeforeRefresh = false;
        }

        private void onlyRefreshWhenClosedChk_Unchecked(object sender, RoutedEventArgs e)
        {
            State.OnlyRefreshWhenClosed = false;
        }


        private void outputToFileChk_Checked(object sender, RoutedEventArgs e)
        {
            State.OutputToFile = true;
            Logger.Instance.RegisterLogger(new FileLogger());
        }

        private void outputToFileChk_Unchecked(object sender, RoutedEventArgs e)
        {
            State.OutputToFile = false;
        }

        #endregion

        private void saveConfigBtn_Click(object sender, RoutedEventArgs e)
        {
            SaveValuesToConfig();
        }

        private void image1_ImageFailed(object sender, ExceptionRoutedEventArgs e)
        {

        }
    }
}
